# -*- coding: utf-8 -*-
"""
    moe.handlers
    ~~~~~~~~~~~~

    Base handlers.

    :copyright: 2009 by tipfy.org.
    :license: BSD, see LICENSE.txt for more details.
"""
import logging

from werkzeug import cached_property, import_string

from tipfy import HTTPException, RequestHandler, Response, Tipfy, abort
from tipfy import i18n
from tipfyext import jinja2
from tipfy import sessions
from tipfy import auth
from tipfy.utils import json_encode

from moe import datastore_cache

# Middleware.
middleware_session = sessions.SessionMiddleware()
middleware_i18n = i18n.I18nMiddleware()
middleware_login_required = auth.LoginRequiredMiddleware()
middleware_user_required = auth.UserRequiredMiddleware()
middleware_user_required_if_authenticated = auth.UserRequiredIfAuthenticatedMiddleware()


class Breadcrumbs(list):
    """A simple container for breadcrumb navigation links."""
    def __init__(self, handler):
        super(Breadcrumbs, self).__init__()
        self.url_for = handler.url_for

    def get(self, text, rulename, **kwargs):
        return text, self.url_for(rulename, **kwargs)

    def add(self, text, rulename, **kwargs):
        super(Breadcrumbs, self).append(self.get(text, rulename, **kwargs))


class CustomJinja2(jinja2.Jinja2):
    def __init__(self, app, _globals=None, filters=None):
        super(CustomJinja2, self).__init__(app, _globals, filters)

        # Global variables and functions available for all templates.
        self.environment.globals.update({
        })


class BaseHandler(RequestHandler, jinja2.Jinja2Mixin):
    """Base for all handlers."""
    middleware = [middleware_session, middleware_i18n,
        middleware_user_required_if_authenticated]

    # The Jinja2 creator.
    jinja2_class = CustomJinja2

    def __init__(self, app, request):
        super(BaseHandler, self).__init__(app, request)

        # Get sitename from config or use host minus port as default
        # sitename.
        self.sitename = self.request.host.rsplit(':', 1)[0]

    @cached_property
    def breadcrumbs(self):
        return Breadcrumbs(self)

    @cached_property
    def messages(self):
        """A list of status messages to be displayed to the user."""
        messages = self.session.get_flashes(key='_messages')
        return [msg for msg, level in messages]

    def render_response(self, filename, **values):
        self.context.update({
            'auth':           self.auth,
            'request':        self.request,
            'breadcrumbs':    self.breadcrumbs,
            'debug':          self.app.debug,
            'sitename':       self.get_config('moe', 'sitename', self.sitename),
            'analytics_code': self.get_config('moe', 'analytics_code', None),
            'apps_installed': self.get_config('tipfy', 'apps_installed'),
            'messages':       json_encode(self.messages),
        })

        return super(BaseHandler, self).render_response(filename,
            **values)

    def head(self, **kwargs):
        """Accept HEAD requests."""
        return Response('')

    def set_form_error(self, body=None, title=None):
        """Adds a form error message.

        :param body:
            Message contents.
        :param title:
            Optional message title.
        :return:
            ``None``.
        """
        if body is None:
            body = i18n._('A problem occurred. Please correct the errors '
                'listed in the form.')

        if title is None:
            title = i18n._('Error')

        self.add_message('error', body, title=title, life=None)

    def add_message(self, level, body, title=None, life=None, flash=False):
        """Adds a status message.

        :param level:
            Message level. Common values are "success", "error", "info" or
            "alert".
        :param body:
            Message contents.
        :param title:
            Optional message title.
        :param life:
            Message life time in seconds. User interface can implement
            a mechanism to make the message disappear after the elapsed time.
            If not set, the message is permanent.
        :return:
            ``None``.
        """
        if level == 'success' and life is None:
            life = 5

        message = {'level': level, 'title': title, 'body': body, 'life': life}
        if flash is True:
            self.session.add_flash(message, key='_messages')
        else:
            self.messages.append(message)


class LoginRequiredHandler(BaseHandler):
    middleware = [middleware_session, middleware_i18n,
        middleware_login_required]


class UserRequiredHandler(BaseHandler):
    middleware = [middleware_session, middleware_i18n,
        middleware_user_required]


class ErrorHandler(BaseHandler):
    middleware = [middleware_session, middleware_i18n]

    def handle_exception(self, exception=None):
        # Always log exceptions.
        logging.exception(exception)

        # Get the exception code and description, if it is an HTTPException,
        # or assume 500.
        code = getattr(exception, 'code', 500)
        message = getattr(exception, 'description', None)

        if self.app.debug and code not in (404,):
            # Raise the exception in dev except for NotFound.
            raise

        if code in (403, 404):
            # Render a special template for these codes.
            template = 'base/error_%d.html' % code
        else:
            # Render a generic 500 template.
            template = 'base/error_500.html'

        # Set breadcrumbs to follow rest of the site.
        self.breadcrumbs.add(i18n._('Home'), 'home/index')

        # Render the template using the exception message, if any, and set
        # the status code.
        response = self.render_response(template, message=message)
        response.status_code = code
        return response
